問題解説: Docker 実技2

問題文

Kubernetes クラスタ環境を移行後、Kubernetes上のWordPressにアクセスできなくなりました。
原因をつきとめ、修正してください。

情報

Kubernetesクラスタの移行手順書は存在しません。アドレスレンジに以下の変更があったことのみ分かっています。

Address Range: 172.16.0.0/24 -> 192.168.0.0/24
Cluster Address Range: 192.168.0.0/24 -> 10.254.0.0/24

  • Address Range: Kubernetes Node に振られるアドレスのレンジ
  • Cluster Address Range: Kubernetes Service に振られるアドレスのレンジ

ゴール

VNC 踏み台サーバ上のブラウザにて「192.168.0.1:30080」を入力することで、WordPressの画面が表示される

トラブルの概要

Kubernetes Pod の名前解決に失敗するため、wordpress Pod のセットアップに失敗する。その結果、外部からWordPressへのhttpアクセスに失敗する。

解説

当問題の環境では、kube-apiserver が掴んでいる証明書の Subject Alternative Names (SANs) が誤っていることが原因で、Pod からsvc/kubernetesへhttpsでの通信が失敗します。そのため svc/kubernetes との通信が必要である kube-dns Pod を正常に動作させることが出来ず、wordpress Pod から mysql.default.svc.cluster.local:3306 への通信における名前解決に失敗するため、wordpress Pod のセットアップに失敗するという問題が生じます。

当問題は、kube-apiserver が正常な SANs を持つ証明書を掴むよう、証明書を再生成することで解決します。

なおKubernetesは、kube-apiserverの --tls-cert-file オプションにサーバ証明書、 --tls-private-key-file オプションに秘密鍵、kube-controller-managerの --root-ca-file オプションにCA証明書を指定することで Podからsvc/kubernetesへのhttps通信が可能となります。

回答例

まず、現在のPodの稼働状況を確認します。

# kubectl get pod
NAME READY STATUS RESTARTS AGE
mysql-755f57f594-mc9cr 1/1 Running 1 2h
wordpress-dc9bb949d-9glcx 1/1 Running 4 2h
wordpress-dc9bb949d-sntj9 1/1 Running 4 2h

アクセス先のPodである pod/wordpress のログを確認します。

# kubectl logs wordpress-dc9bb949d-9glcx
WordPress not found in /var/www/html - copying now...
Complete! WordPress has been successfully copied to /var/www/html

Warning: mysqli::__construct(): php_network_getaddresses: getaddrinfo failed: Temporary failure in name resolution in Standard input code on line 22

Warning: mysqli::__construct(): (HY000/2002): php_network_getaddresses: getaddrinfo failed: Temporary failure in name resolution in Standard input code on line 22

MySQL Connection Error: (2002) php_network_getaddresses: getaddrinfo failed: Temporary failure in name resolution
... (省略)

以上のログより名前解決に失敗していることがわかります。/root/manifests/wordpress/deployment-wordpress.yaml の中身を見ると、WORDPRESS_DB_HOST 環境変数に mysql.default.svc.cluster.local というドメイン名が渡されているため、当箇所における名前解決に失敗しているものであると考えられます。

Kubernetesクラスタ内のServiceリソースの名前解決は kube-system ネームスペース内の kube-dns Pod が行います。

kube-dns Pod の稼働状況を確認します。

# kubectl get pod -n kube-system
NAME READY STATUS RESTARTS AGE
kube-dns-7c7877989-8frxc 2/3 Running 1 1m

kube-dns Pod のログを確認します。

# kubectl -n kube-system logs kube-dns-7c7877989-8frxc kubedns
...(省略)
E0729 13:29:49.132142 1 reflector.go:201] k8s.io/dns/pkg/dns/dns.go:192: Failed to list *v1.Service: Get https://10.254.0.1:443/api/v1/services?resourceVersion=0: x509: certificate is valid for 172.16.0.1, 192.168.0.1, not 10.254.0.1
E0729 13:29:49.139524 1 reflector.go:201] k8s.io/dns/pkg/dns/dns.go:189: Failed to list *v1.Endpoints: Get https://10.254.0.1:443/api/v1/endpoints?resourceVersion=0: x509: certificate is valid for 172.16.0.1, 192.168.0.1, not 10.254.0.1
...(省略)

以上のエラーログより、kube-apiserverの持つ証明書のSANsに10.254.0.1が存在しないことがわかります。

サーバ証明書に対応する秘密鍵である /etc/pki/tls/kube01/server.key を用いてCSRファイルを再生成します。以下はopensslを用いた例です。

# cat << 'EOF' > /etc/pki/tls/kube01/csr.conf
[ req ]
default_bits = 2048
prompt = no
default_md = sha256
req_extensions = req_ext
distinguished_name = dn

[ dn ]
C = JP
ST = dummy
L = dummy
O = dummy
OU = dummy
CN = dummy

[ req_ext ]
subjectAltName = @alt_names

[ alt_names ]
DNS.1 = kubernetes
DNS.2 = kubernetes.default
DNS.3 = kubernetes.default.svc
DNS.4 = kubernetes.default.svc.cluster
DNS.5 = kubernetes.default.svc.cluster.local
DNS.6 = kube01
IP.1 = 192.168.0.1
IP.2 = 10.254.0.1

[ v3_ext ]
authorityKeyIdentifier=keyid,issuer:always
basicConstraints=CA:FALSE
keyUsage=keyEncipherment,dataEncipherment
extendedKeyUsage=serverAuth,clientAuth
subjectAltName=@alt_names
EOF
# openssl req -new -key server.key -out server.csr -config csr.conf

CSRファイルに対し、CA証明書である/etc/pki/tls/kube01/ca.crt にて署名を行うことでCRTファイルを生成します。

# openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key \
-CAcreateserial -out server.crt -days 10000 \
-extensions v3_ext -extfile csr.conf

kube-apiserver.service を再起動します。

# systemctl restart kube-apiserver.service

Pod を明示的に再起動させるため、Pod を再作成します。

# kubectl delete -f /root/manifests/kube-dns/kube-dns.yml &amp;amp;amp;&amp;amp;amp; kubectl create -f /root/manifests/kube-dns/kube-dns.yml
# kubectl delete -f /root/manifests/wordpress/wordpress.yaml &amp;amp;amp;&amp;amp;amp; kubectl create -f /root/manifests/wordpress/wordpress.yaml

講評

Kubernetesは利用者が簡単にコンテナのオーケストレーションを行える反面、内部の仕組みは複雑なものになっています。そのためKubernetesについて学ぶ際は、GCP などのクラウドサービスにてKuberntesを利用する他、Kubernetesを実際に構築してみることに是非チャレンジすると良いと思います!